import { $set } from '@feng3d/serialization';
import { Color, MeshLambertMaterial3D, Node3D, PerspectiveCamera3D, Plane, Scene3D, SphereGeometry3D, Vector3, WebGLRenderer3D } from 'feng3d-three';
import GUI from 'lil-gui';
import '../../extension/controls/OrbitControls3D';

let camera: PerspectiveCamera3D;
let scene: Scene3D;
let renderer: WebGLRenderer3D;

const params = {
    clipIntersection: true,
    planeConstant: 0,
    showHelpers: false
};

const clipPlanes = [
    new Plane(new Vector3(1, 0, 0), 0),
    new Plane(new Vector3(0, -1, 0), 0),
    new Plane(new Vector3(0, 0, -1), 0)
];

init();
render();

function init()
{
    renderer = new Node3D().addComponent('WebGLRenderer3D', { parameters: { antialias: true } });
    renderer.setPixelRatio(window.devicePixelRatio);
    renderer.setSize(window.innerWidth, window.innerHeight);
    renderer.localClippingEnabled = true;
    document.body.appendChild(renderer.domElement);

    scene = renderer.addComponent('Scene3D');

    camera = new Node3D().addComponent('PerspectiveCamera3D', { fov: 40, aspect: window.innerWidth / window.innerHeight, near: 1, far: 200 });

    camera.entity.position.set(-1.5, 2.5, 3.0);

    const controls = scene.addComponent('OrbitControls3D', { camera, domElement: renderer.domElement });
    controls.emitter.on('change', render); // use only if there is no animation loop
    controls.minDistance = 1;
    controls.maxDistance = 10;
    controls.enablePan = false;

    const light = new Node3D().addComponent('HemisphereLight3D', { color: { value: 0xffffff }, groundColor: { value: 0x080808 }, intensity: 1.5 });
    light.entity.position.set(-1.25, 1, 1.25);
    scene.entity.addChild(light.entity);

    //     // const helper = new THREE.CameraHelper( light.shadow.camera );
    //     // scene.add( helper );

    //

    const group = new Node3D();

    for (let i = 1; i <= 30; i += 2)
    {
        const geometry = $set(new SphereGeometry3D(), { radius: i / 30, widthSegments: 48, heightSegments: 24 });

        const material = $set(new MeshLambertMaterial3D(), {
            color: new Color().setHSL(Math.random(), 0.5, 0.5),
            side: 'DoubleSide',
            clippingPlanes: clipPlanes,
            clipIntersection: params.clipIntersection
        });

        group.addChild(new Node3D().addComponent('Mesh3D', { geometry, material }).entity);
    }

    scene.entity.addChild(group);

    // helpers

    const helpers = new Node3D();
    helpers.addChild(new Node3D().addComponent('PlaneHelper3D', { plane: clipPlanes[0], size: 2, hex: 0xff0000 }).entity);
    helpers.addChild(new Node3D().addComponent('PlaneHelper3D', { plane: clipPlanes[1], size: 2, hex: 0x00ff00 }).entity);
    helpers.addChild(new Node3D().addComponent('PlaneHelper3D', { plane: clipPlanes[2], size: 2, hex: 0x0000ff }).entity);
    helpers.visible = false;
    scene.entity.addChild(helpers);

    // gui

    const gui = new GUI();

    gui.add(params, 'clipIntersection').name('clip intersection').onChange(function (value)
    {
        const children = group.children;

        for (let i = 0; i < children.length; i++)
        {
            const mesh = children[i].getComponent('Mesh3D');
            if (mesh)
            {
                mesh.material.clipIntersection = value;
            }
        }

        render();
    });

    gui.add(params, 'planeConstant', -1, 1).step(0.01).name('plane constant').onChange(function (value)
    {
        for (let j = 0; j < clipPlanes.length; j++)
        {
            clipPlanes[j].constant = value;
        }

        render();
    });

    gui.add(params, 'showHelpers').name('show helpers').onChange(function (value)
    {
        helpers.visible = value;

        render();
    });

    //

    window.addEventListener('resize', onWindowResize);
}

function onWindowResize()
{
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();

    renderer.setSize(window.innerWidth, window.innerHeight);

    render();
}

function render()
{
    renderer.render(scene, camera);
}
